`include "defines.v"

module arbiter(

    input wire            clock,    
    input wire            reset,
    //pc
    input  wire           if_read_req_i,
    input  wire           if_valid_i,
    output reg            if_ready_o,
    output wire [63:0]    if_data_read_o,
    input  wire [63:0]    if_addr_i,
    input  wire [1 :0]    if_size,

    //mem
    input  wire           mem_read_req_i,
    input  wire           mem_write_req_i,
    input  wire           mem_valid_i,
    output wire           mem_ready_o,
    input  wire [63:0]    mem_addr_i,
    input  wire [1 :0]    mem_size_i,
    output wire [63:0]    mem_data_read_o,
    input  wire [63:0]    mem_data_write_i,

    //axi   
	output reg            rw_valid_o,
	input  wire           rw_ready_i,
    output wire           rw_req_o,
    input  wire [63:0]    data_read_i,
    output wire [63:0]    data_write_o,
    output wire [63:0]    rw_addr_o,
    output wire [1 :0]    rw_size_o,
    //state
    output reg            arbiter_state_o
    
);
    reg [0:0] arbiter_state;
    assign arbiter_state_o = arbiter_state;
    /*
    //arbiter state machine
    parameter [1:0] ARBITER_IDLE = 2'b00;
    parameter [1:0] ARBITER_IF   = 2'b01;
    parameter [1:0] ARBITER_MEM  = 2'b10;
    //parameter [1:0] ARBITER_WAIT = 2'b11;

    always @(posedge clock) 
        begin
            if (reset) 
                arbiter_state <= ARBITER_IDLE;
            else
                begin
                    case(arbiter_state)
                        ARBITER_IDLE:
                            begin
                                if(mem_valid_i)
                                    arbiter_state <= ARBITER_MEM;  
                                else if(if_valid_i)
                                    arbiter_state <= ARBITER_IF;
                                else
                                    arbiter_state <= arbiter_state;  
                            end
                        ARBITER_IF:
                            begin
                                if(trans_done_i)
                                    arbiter_state <= ARBITER_IDLE;
                                else
                                    arbiter_state <= arbiter_state;     
                            end
                        ARBITER_MEM:
                            begin
                                if(trans_done_i)
                                    arbiter_state <= ARBITER_IDLE;
                                else
                                    arbiter_state <= arbiter_state; 
                            end
                        default:
                            arbiter_state <= arbiter_state;
                    endcase
                end
        end
    */
        //arbiter state machin
    parameter [0:0] ARBITER_IF   = 1'b0;
    parameter [0:0] ARBITER_MEM  = 1'b1;

    always @(posedge clock) 
        begin
            if (reset == `RST) 
                arbiter_state <= ARBITER_IF;
            else
                begin
                    case(arbiter_state)
                        ARBITER_IF:
                            begin
                                if(if_ready_o && mem_valid_i)
                                    arbiter_state <= ARBITER_MEM;
                                else
                                    arbiter_state <= arbiter_state;     
                            end
                        ARBITER_MEM:
                            begin
                                if(mem_ready_o && if_valid_i)
                                    arbiter_state <= ARBITER_IF;
                                else
                                    arbiter_state <= arbiter_state; 
                            end
                        default:
                            arbiter_state <= arbiter_state;
                    endcase
                end
        end

    //axi
    always@(*)
    begin
        if (reset == `RST) 
            begin
                if_ready_o      = 1'b0;
                if_data_read_o  = 64'b0;
                mem_ready_o     = 1'b0;
                mem_data_read_o = 64'b0;
                rw_valid_o      = 1'b0;
                rw_req_o        = 1'b0;
                data_write_o    = 64'b0;
                rw_addr_o       = 64'b0;
                rw_size_o       = 2'b0;
            end
        else if(arbiter_state == ARBITER_IF)
            begin
                if_ready_o      = rw_ready_i;
                if_data_read_o  = data_read_i;
                mem_ready_o     = 1'b0;
                mem_data_read_o = 64'b0;
                rw_valid_o      = if_valid_i;
                rw_req_o        = `REQ_READ;
                data_write_o    = 64'b0;
                rw_addr_o       = if_addr_i;
                rw_size_o       = if_size;
            end
        else if(arbiter_state == ARBITER_MEM)
            begin
                if_ready_o      = 1'b0;
                if_data_read_o  = 64'b0;
                mem_ready_o     = rw_ready_i;
                mem_data_read_o = data_read_i;
                rw_valid_o      = mem_valid_i;
                rw_req_o        = mem_read_req_i ? `REQ_READ : `REQ_WRITE;
                data_write_o    = mem_data_write_i;
                rw_addr_o       = mem_addr_i;
                rw_size_o       = mem_size_i; 
            end
        else
            begin
                if_ready_o      = 1'b0;
                if_data_read_o  = 64'b0;
                mem_ready_o     = 1'b0;
                mem_data_read_o = 64'b0;
                rw_valid_o      = 1'b0;
                rw_req_o        = 1'b0;
                data_write_o    = 64'b0;
                rw_addr_o       = 64'b0;
                rw_size_o       = 2'b0;
            end  
    end


endmodule